﻿using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpExt.Collections
{
    /*
     * <rule> ::= <step> (',' <step>)*
     * <step> ::= I ('-' I ('/' I))
     * 
     * I: potrebbe essere una stringa che rappresenta un mese o un giorno della settimana
     * 
     * Gennaio:   1
     * Dicembre: 12
     * 
     * Domenica:  0
     * Sabato:    6
     */


    public struct BitSet
    {
        #region Constants

        public static String[] MONTH_NAMES_IT =
            {
                "meseSconosciuto",
                "gennaio",
                "febbraio",
                "marzo",
                "aprile",
                "maggio",
                "giugno",
                "luglio",
                "agosto",
                "settembre",
                "ottobre",
                "novembre",
                "dicembre"
            };

        public static String[] MONTH_NAMES_EN =
            {
                "unknownMonth",
                "january",
                "february",
                "march",
                "april",
                "may",
                "june",
                "july",
                "august",
                "september",
                "october",
                "november",
                "december"
            };

        public static String[][] MONTH_NAMES =
            {
                MONTH_NAMES_IT,
                MONTH_NAMES_EN
            };


        public static String[] WEEKDAY_NAMES_IT =
            {
                "domenica",
                "lunedi",
                "martedi",
                "mercoledi",
                "giovedi",
                "venerdi",
                "sabato",
                "giornoDellaSettimanaSconosciuto"
            };

        public static String[] WEEKDAY_NAMES_EN =
            {
                "sunday",
                "monday",
                "tuesday",
                "wednesday",
                "thursday",
                "friday",
                "saturday",
                "unknownWeekDay"
            };

        public static String[][] WEEKDAY_NAMES =
            {
                WEEKDAY_NAMES_IT,
                WEEKDAY_NAMES_EN
            };

        public static String[][] ALL_NAMES =
            {
                MONTH_NAMES_IT,
                MONTH_NAMES_EN,
                WEEKDAY_NAMES_IT,
                WEEKDAY_NAMES_EN
            };

        #endregion

        #region Static Operations

        public static BitSet Parse(String pattern)
        {
            return Parse(pattern, 64, 0);
        }

        public static BitSet Parse(String pattern, int length)
        {
            return Parse(pattern, length, 0);
        }

        public static BitSet Parse(String pattern, int length, int startAt)
        {
            BitSetParser bsp = new BitSetParser()
                                   {
                                       Pattern = pattern,
                                       StartAt = startAt,
                                       Length = length,
                                   };
            return bsp.Parse();
        }

        public static BitSet Parse(String pattern, String[][] names)
        {
            return Parse(pattern, names, 0);
        }

        public static BitSet Parse(String pattern, String[][] names, int startAt)
        {
            BitSetParser bsp = new BitSetParser()
                                   {
                                       Pattern = pattern,
                                       StartAt = startAt,
                                       Names = names,
                                   };
            return bsp.Parse();
        }


        public static BitSet All(int length)
        {
            return All(length, 0);
        }

        public static BitSet All(int length, int start)
        {
            BitSet bt = new BitSet(length);
            for(int i=start; i<length+start; ++i)
                bt.Set(i);
            return bt;
        }

        #endregion 

        #region Private Data

        private int _length;
        private long _bits;
        private int _start;

        #endregion

        #region Constructor

        public BitSet(int length)
        {
            _length = length;
            _bits = 0;
            _start = 0;
        }

        public BitSet(int length, int start)
        {
            _length = length;
            _bits = 0;
            _start = start;
        }

        #endregion

        #region Properties

        public int Start
        {
            get { return _start; }
        }

        public int Length
        {
            get { return _length; }
        }

        public long Bits
        {
            get { return _bits; }
        }

        #endregion

        #region Operations

        public void Set(int bit)
        {
            _bits |= 1L << bit;
        }

        public void Clear(int bit)
        {
            _bits &= ~(1L << bit);
        }

        public bool Test(int bit)
        {
            return (_bits & (1L << bit)) != 0;
        }

        #endregion

        #region Overrides

        public override String ToString()
        {
            StringBuilder sb = new StringBuilder("{");

            for (int i = 0; i < _length; ++i)
            {
                if(!Test(i))
                    continue;

                if (sb.Length != 1)
                    sb.Append(", ");

                sb.Append(i);
            }

            sb.Append("}");
            return sb.ToString();
            //return BitSetFormatter.Format(this);
        }

        public String ToString(String[] names)
        {
            StringBuilder sb = new StringBuilder("{");

            for (int i = 0; i < _length; ++i)
            {
                if (!Test(i))
                    continue;

                if (sb.Length != 1)
                    sb.Append(", ");

                sb.Append(names[i]);
            }

            sb.Append("}");
            return sb.ToString();
        }

        #endregion
    }

    class BitSetParser
    {
        #region Private Data

        private String[][] _names;
        private BitSet _bitset;
        private String _pattern;
        private int _at;
        private int _plength;

        private int _startAt;
        private int _length;

        #endregion 

        #region Constructor

        internal BitSetParser()
        {
        }

        #endregion

        #region Properties

        public String Pattern
        {
            set
            {
                _pattern = value;
                _at = 0;
                _plength = _pattern.Length;
            }
        }

        public int StartAt
        {
            set { _startAt = value; }
        }

        public int Length
        {
            set { _length = value; }
        }

        public String[][] Names
        {
            set
            {
                _names = value;
                _length = _names[0][0].Length;
            }
        }

        #endregion

        #region Operations

        internal BitSet Parse()
        {
            _bitset = new BitSet(_length, _startAt);

            while(_at < _plength)
            {
                parseStep();
                char ch = getch();
                if(ch != ',')
                    break;
            }

            return _bitset;
        }

        private void parseStep()
        {
            int i1 = parseInt();

            char c1 = getch();
            if(c1 == '*')
            {
                for (int i = i1; i <= _bitset.Length; i++)
                {
                    _bitset.Set(i);
                }
            }
            else if(c1 == '-')
            {
                int i2 = parseInt();

                char c2 = getch();
                if(c2 == '/')
                {
                    int s = parseInt();
                    for (int i = i1; i <= i2; i += s)
                    {
                        _bitset.Set(i);
                    }
                }
                else
                {
                    for (int i = i1; i <= i2; i++)
                    {
                        _bitset.Set(i);
                    }
                }
            }
            else
            {
                ungetch();
                _bitset.Set(i1);
            }
        }

        #endregion

        #region Implementation

        private char getch()
        {
            char ch = chr();
            while(ch == ' ')
                ch = chr();
            return ch;
        }

        private char chr()
        {
            if(_at < _plength)
                return _pattern[_at++];

            ++_at;
            return (char)0;
        }

        private void ungetch()
        {
            --_at;
        }

        private int parseInt()
        {
            char ch = getch();
            ungetch();

            if (ch <= '9')
                return parseNumber();
            else
                return parseName();
        }

        private int parseNumber()
        {

            int i = 0;
            for (char ch = getch(); ch >= '0' && ch <= '9'; ch = getch())
            {
                i *= 10;
                i += (ch - '0');
            }

            ungetch();
            return i;
        }

        private int parseName()
        {
            String name = "";
            for (char ch = getch(); ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z'; ch = getch())
            {
                name += ch;
            }

            ungetch();
            name = name.ToLower();

            for (int i = 0; i < _names.Length; i++)
            {
                for (int j = 0; j < _names[i].Length; j++)
                {
                    if (_names[i][j].StartsWith(name))
                        return j;
                }
            }

            return 0;
        }

        #endregion
    }
}
